package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.aliyun.iot20180120.Client;
import com.aliyun.iot20180120.models.*;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.zzyl.base.PageResponse;
import com.zzyl.base.ResponseResult;
import com.zzyl.constant.CacheConstants;
import com.zzyl.dto.DeviceDto;
import com.zzyl.dto.DevicePageQueryDto;
import com.zzyl.entity.Device;
import com.zzyl.enums.BasicEnum;
import com.zzyl.enums.DeviceLocationTypeEnum;
import com.zzyl.enums.DevicePhysicalLocationTypeEnum;
import com.zzyl.exception.BaseException;
import com.zzyl.mapper.DeviceMapper;
import com.zzyl.properties.AliIoTConfigProperties;
import com.zzyl.service.DeviceService;
import com.zzyl.utils.UserThreadLocal;
import com.zzyl.vo.DeviceVo;
import com.zzyl.vo.ProductVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class DeviceServiceImpl implements DeviceService {
    @Autowired
    private AliIoTConfigProperties aliIoTConfigProperties;
    @Autowired
    private Client client;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private DeviceMapper deviceMapper;

    /**
     * 从物联网平台同步产品列表
     *
     * @throws Exception
     */
    @Override
    public void syncProductList() throws Exception {
        //2.根据idea的提示创建request对象
        QueryProductListRequest request = new QueryProductListRequest();

        //3.设置参数（规则：能设置上的参数都设置上 没有办法设置的就不管）
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        request.setCurrentPage(1);
        request.setPageSize(200);

        //1.根据核心对象调用功能方法
        QueryProductListResponse response = client.queryProductList(request);
        if (!response.getBody().success) {
            throw new BaseException(BasicEnum.IOT_LIST_PRODUCT_ERROR);
        }

        //4.解析结果
        List<QueryProductListResponseBody.QueryProductListResponseBodyDataListProductInfo> productInfo =
                response.getBody().getData().getList().getProductInfo();
        List<ProductVo> voList = productInfo.stream().map(p -> {
            ProductVo vo = BeanUtil.toBean(p, ProductVo.class);
            return vo;
        }).collect(Collectors.toList());

        //5.存储到redis中
        redisTemplate.opsForValue().set(CacheConstants.ALL_PRODUCT_KEY, JSONUtil.toJsonStr(voList));
    }

    /**
     * 查询所有产品列表
     */
    @Override
    public List<ProductVo> allProduct() {
        //1.从redis中获取数据
        String json = redisTemplate.opsForValue().get(CacheConstants.ALL_PRODUCT_KEY);
        //2.判断是否为空，为空的话则返回一个空的List集合
        if (ObjectUtil.isEmpty(json)) {
            return Collections.emptyList();
        }
        //3.不为空则将数据转换成List集合
        List<ProductVo> list = JSONUtil.toList(json, ProductVo.class);
        //4.返回结果
        return list;
    }

    /**
     * 新增设备
     *
     * @param deviceDto
     */
    @Override
    public void registerDevice(DeviceDto deviceDto) {
        //1. 校验设备的名字是否使用过了 如果使用了 则抛出异常
        Integer count = deviceMapper.countName(deviceDto.getDeviceName());
        if (count > 0) {
            throw new BaseException(BasicEnum.DEVICE_NAME_EXIST);
        }

        //2. 新增设备到iot平台
        RegisterDeviceRequest request = deviceDto.getRegisterDeviceRequest();
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        RegisterDeviceResponse response = null;
        try {
            response = client.registerDevice(request);
        } catch (Exception e) {
            //3. 校验新增是否成功 如果失败 则抛出异常 - 主要针对的是网络异常 超时等异常情况
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }

        //3. 校验新增是否成功 如果失败 则抛出异常 - 主要针对的是请求掉通了 但是阿里云因为各种原因注册失败了
        if (response == null || !response.getBody().success) {
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }

        //4. 判断设备的安装位置是否是老人 如果是 则需要把physical_location_type 设置为-1
        if (deviceDto.getLocationType().equals(DeviceLocationTypeEnum.PORTABLE_DEVICE.getOrdinal())) {
            deviceDto.setPhysicalLocationType(DevicePhysicalLocationTypeEnum.ELDER.getOrdinal());
        }

        //5. 到iot平台查询设备的详细信息
        QueryProductRequest req = new QueryProductRequest();
        req.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        req.setProductKey(deviceDto.getProductKey());
        try {
            QueryProductResponse productResponse = client.queryProduct(req);
            if (null != productResponse && productResponse.getBody().success) {
                String productName = productResponse.getBody().getData().getProductName();
                //6. 分装pojo(deviceDto,设备的详细信息)
                Device device = BeanUtil.toBean(deviceDto, Device.class);
                device.setProductName(productName);
                device.setHaveEntranceGuard(0);
                device.setCreateTime(LocalDateTime.now());
                device.setUpdateTime(LocalDateTime.now());
                device.setIotId(response.getBody().getData().getIotId());
                //7. 调用mapper 插入设备数据
                deviceMapper.insert(device);
            } else {
                throw new RuntimeException();
            }

        } catch (Exception e) {
            //8. 如果新增失败 则删除iot平台新增的设备
            DeleteDeviceRequest deleteReq = new DeleteDeviceRequest();
            deleteReq.setDeviceName(deviceDto.getDeviceName());
            deleteReq.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
            deleteReq.setProductKey(deviceDto.getProductKey());
            deleteReq.setIotId(response.getBody().getData().getIotId());

            try {
                client.deleteDevice(deleteReq);
            } catch (Exception ex) {
                //如果删除失败了则需要手动删除
                throw new BaseException(BasicEnum.IOT_DELETE_DEVICE_ERROR);
            }
        }


    }


    /**
     * 分页查询
     *
     * @param devicePageQueryDto
     * @return
     */
    @Override
    public PageResponse<DeviceVo> pageQueryDevice(DevicePageQueryDto devicePageQueryDto) {
        PageHelper.startPage(devicePageQueryDto.getPageNum(), devicePageQueryDto.getPageSize());
        Page<DeviceVo> list = deviceMapper.pageQueryDevice(devicePageQueryDto);
        return PageResponse.of(list, DeviceVo.class);
    }

    /**
     * 查询设备数据的详细信息
     */
    @Override
    public DeviceVo deviceService(QueryDeviceDetailRequest request) throws Exception {
        //1.调用iot平台查询设备详情
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        QueryDeviceDetailResponse response = client.queryDeviceDetail(request);
        if (response.getBody().success) {
            //2.通过iotid查询数据库中的设备数据
            Device device = deviceMapper.deviceService(request.getIotId());
            //3.合并数据并返回
            DeviceVo deviceVo = BeanUtil.toBean(device, DeviceVo.class);
            BeanUtil.copyProperties(response.getBody().getData(), deviceVo, CopyOptions.create().ignoreNullValue());

            return deviceVo;
        }
        return null;
    }

    /**
     * 查询指定设备的物模型运行状态
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult queryDevicePropertyStatus(DeviceDto dto) throws Exception {

        QueryDevicePropertyStatusRequest request = new QueryDevicePropertyStatusRequest();
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        request.setDeviceName(dto.getDeviceName());
        request.setProductKey(dto.getProductKey());

        QueryDevicePropertyStatusResponse response = client.queryDevicePropertyStatus(request);

        if (response.getBody().success) {
            return ResponseResult.success(response.getBody().getData());
        }
        return null;
    }

    @Override
    public ResponseResult queryThingModelPublished(DeviceDto dto) throws Exception {
        QueryThingModelPublishedRequest request = new QueryThingModelPublishedRequest();
        request.setProductKey(dto.getProductKey());
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        QueryThingModelPublishedResponse response = client.queryThingModelPublished(request);
        if (response.getBody().success) {
            return ResponseResult.success(response.getBody().getData());
        }
        return null;
    }

    @Override
    public void updateDevice(DeviceDto dto) throws Exception {
        List<BatchUpdateDeviceNicknameRequest.BatchUpdateDeviceNicknameRequestDeviceNicknameInfo> list = new ArrayList<>();
        BatchUpdateDeviceNicknameRequest.BatchUpdateDeviceNicknameRequestDeviceNicknameInfo info = BeanUtil.toBean(dto, BatchUpdateDeviceNicknameRequest.BatchUpdateDeviceNicknameRequestDeviceNicknameInfo.class);
        list.add(info);
        BatchUpdateDeviceNicknameRequest request = new BatchUpdateDeviceNicknameRequest();
        request.setDeviceNicknameInfo(list);
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        BatchUpdateDeviceNicknameResponse response = client.batchUpdateDeviceNickname(request);

        if (!response.getBody().success) {
            throw new BaseException(BasicEnum.IOT_BATCH_UPDATE_DEVICE_ERROR);
        }
        Device bean = BeanUtil.toBean(dto, Device.class);
        bean.setUpdateBy(UserThreadLocal.get());
        deviceMapper.updateDevice(bean);

    }

    /**
     * 删除设备
     * @param dto
     */
    @Override
    public void deleteDevice(DeviceDto dto) {

        DeleteDeviceRequest request = new DeleteDeviceRequest();
        request.setIotId(dto.getIotId());
        request.setProductKey(dto.getProductKey());
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());


        DeleteDeviceResponse response = null;
        try {
            response = client.deleteDevice(request);
            if (!response.getBody().success){
                throw new RuntimeException();
            }
            deviceMapper.deleteDevice(dto.getIotId());
        } catch (Exception e) {
            throw new BaseException(BasicEnum.IOT_DELETE_DEVICE_ERROR);
        }

    }
}
